//CAS C INTERFACE {

#include <tbb/tbb.h>
#include <assert.h>
#include <time.h>
#include "mutex.h"

typedef void * atomic_counter;

using namespace tbb;


/**

 * Return the new atomic counter on success or NULL on memory allocation error.

 * Also the counter is initialized to 0.

 *

 * @return The atomic counter

 */

static void *ose_one_allocate_atomic_counter(void)

{
	atomic<int> *ret = new atomic<int>();
	return ret;
}


/**

 * Free the atomic counter.

 *

 * @param atomic_ctr The counter to free.

 */

static void ose_one_free_atomic_counter(atomic_counter atomic_ctr)

{
	atomic<int> *ctr = (atomic<int> *)atomic_ctr;
	assert(ctr != NULL);
	delete ctr;

}


/**

 * Denote val as the value of the atomic_ctr.

 * If val==compare then val = swap is done.

 * That is if val equals compare then it is set to swap.

 * In any case the old val is returned.

 *ose_one_allocate_atomic_counter(void)

 * THIS ACTION IS ATOMIC!

 * That means that the compare and the swap are done atomically (i.e. no other change

 * to the value of 	the counter can occur between them - only before or after both

 * actions have been done).

 *

 * @param atomic_ctr The counter to (possibly) change and return the value of.
ose_one_allocate_atomic_counter(void)
 * @param compare The value to base the change decision on.

 * @param swap The value to swap in case the compare equals the contents of the counter.

 *

 * @return The old value of atomic_ctr (before this function changed it, if it did).

 */

static int ose_one_compare_and_set_atomic_counter(

		atomic_counter atomic_ctr,
		int compare,
		int swap)

{
	atomic<int> *ctr = (atomic<int> *)atomic_ctr;
	assert(ctr != NULL);
	return ctr->compare_and_swap(swap, compare);

}



//} CAS C INTERFACE

Mutex *createMutex(){
	Mutex *m = (Mutex *)malloc(sizeof(Mutex));
	if (m == NULL) return NULL;
	
	m->req = (struct timespec *)malloc(sizeof(struct timespec));
	if (m->req == NULL) {
		free(m);
		return NULL;
	}
	m->rem = (struct timespec *)malloc(sizeof(struct timespec));
		if (m->rem == NULL) {
			free(m->req);
			free(m);
			return NULL;
		}
	
		m->req->tv_sec = 0.000000001;
		m->req->tv_nsec = 0.000000005;
		
		m->req->tv_sec = 0.00000001;
		m->req->tv_nsec = 0.000000005;
	
	m->flag = (atomic_counter *)ose_one_allocate_atomic_counter();
	
	return m;
}

void destroyMutex(Mutex *m){
	
	free(m->rem);
	free(m->req);
	ose_one_free_atomic_counter(m->flag);
	free(m);
}

void mutexLock(Mutex *m){
	while(ose_one_compare_and_set_atomic_counter(m->flag, 0, 1)){
		nanosleep(m->req, m->rem);
	}
}

void mutexUnlock(Mutex *m){
	((atomic<int> *)m->flag)->compare_and_swap(0, 1);
}
